package com.github.cosycode.imgtool.util;

import com.github.cosycode.common.lang.BaseRuntimeException;
import com.github.cosycode.common.util.encode.MessageDigestUtils;
import com.github.cosycode.common.util.io.FileSystemUtils;
import com.github.cosycode.common.util.io.IoUtils;
import com.github.cosycode.common.util.otr.PrintTool;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Files;
import java.util.*;

/**
 * <b>Description : </b>
 * <p>
 * <b>created in </b> 2022/5/16
 * </p>
 *
 * @author CPF
 * @since 1.0
 **/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils {

    public static void writeFile(File file, String newContent) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(file)) {
            LogUtils.logEvent(() -> {
                IoUtils.writeStringToOutputStream(fos, newContent);
            }, "write file content: {} ", file.getCanonicalPath());
        }
    }

    public static void copyFile(File source, File target, boolean dryRun) throws IOException {
        LogUtils.logEvent(() -> {
            if (!target.exists()) {
                FileSystemUtils.insureFileDirExist(target.getParentFile());
                if (!dryRun) {
                    Files.copy(source.toPath(), target.toPath());
                }
            } else {
                if (target.length() == source.length()) {
                    log.warn("文件已经存在, 且文件长度相同 : {}", target);
                } else {
                    log.warn("文件已经存在, 且文件不同 : {}", target);
                }
            }
        }, "{} file: {} ", "copy", source.getPath());
    }

    public static void moveFile(File source, File target, boolean dryRun) throws IOException {
        LogUtils.logEvent(() -> {
            if (!target.exists()) {
                FileSystemUtils.insureFileDirExist(target.getParentFile());
                if (!dryRun) {
                    Files.move(source.toPath(), target.toPath());
                }
            } else {
                if (target.length() == source.length()) {
                    log.warn("文件已经存在, 且文件长度相同 : {}", target);
                } else {
                    log.warn("文件已经存在, 且文件不同 : {}", target);
                }
            }
        }, "{} file: {} ", "move", source.getPath());
    }

    public static void moveFileToDir(File source, File targetDir, boolean move) throws IOException {
        File targetFile = new File(targetDir.getPath() + File.separator + source.getName());
        moveFile(source, targetFile, move);
    }

    public static void deleteFile(@NonNull File source) throws IOException {
        String absolutePath = source.getAbsolutePath();
        if (!source.isFile()) {
            throw new BaseRuntimeException("非文件, 不能删除: " + absolutePath);
        }
        if (!source.exists()) {
            log.info("删除失败, 文件已经不存在 => {}", absolutePath);
            return;
        }
        if (source.delete()) {
            log.info("文件删除成功 => {}", absolutePath);
        } else {
            log.info("文件删除失败 => {}", absolutePath);
        }
    }

    /**
     * 通过 Md5 对比, 若集合中文件中有重复数据, 则随机删除重复的文件, 保留最终的一个文件是唯一的
     *
     * @param filePathColl 文件路径集合
     * @param dryRun       dryRun
     */
    public static void clearRepeatFileByMd5(Collection<String> filePathColl, boolean dryRun) {
        boolean allSame = true;
        Map<String, List<String>> md5$filepath$map = new HashMap<>();
        for (String filePath : filePathColl) {
            try {
                String md5 = MessageDigestUtils.getMD5(new File(filePath));
                PrintTool.info("  md5: {}, path: {}", md5, filePath);
                List<String> stringList = md5$filepath$map.computeIfAbsent(md5, k -> new ArrayList<>());
                stringList.add(filePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        int uniqueCnt = md5$filepath$map.size();
        int totalCnt = filePathColl.size();
        PrintTool.info("[file Md5 compare info] total: {}, itemFile: {}, needDelete: {}", totalCnt, uniqueCnt, totalCnt - uniqueCnt);
        for (Map.Entry<String, List<String>> entry : md5$filepath$map.entrySet()) {
            List<String> strings = entry.getValue();
            int size = strings.size();
            PrintTool.info(" md5: {}, total: {}, delete:{}, retainFile ==> {}", entry.getKey(), size, size - 1, strings.get(0));
            for (int i = 1; i < size; i++) {
                String filePath = strings.get(i);
                if (dryRun) {
                    PrintTool.warn(" [DryRun] delete file => {}", filePath);
                } else {
                    try {
                        FileUtils.deleteFile(new File(filePath));
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * 下载指定文件下的网络图片. 并保存到相应文件夹, 更改 md 文件
     */
    public static List<File> findFileList(File[] searchDir, FileFilter fileFilter) throws FileNotFoundException {
        // 1. 遍历文件夹, 找出符合条件的文件
        Set<String> filePath = new HashSet<>();
        List<File> files = new ArrayList<>();
        for (File dir : searchDir) {
            FileSystemUtils.findFileFromDir(dir, fileFilter, true, file -> {
                final String path = file.getPath();
                if (!filePath.contains(path)) {
                    filePath.add(path);
                    files.add(file);
                }
            });
        }
        return files;
    }
}
